// system.go
package main

import (
	"crypto/sha256"
	"fmt"
	"math/big"

	pbc "github.com/Nik-U/pbc"
)

// SystemParams 存储所有参与者共享的公共参数
type SystemParams struct {
	Pairing *pbc.Pairing
	G       *pbc.Element // G1的生成元
	U       *pbc.Element // G1中的一个随机元素
}

// PublicKey 数据所有者的公钥
type PublicKey struct {
	V *pbc.Element // v = g^x
}

// SecretKey 数据所有者的私钥
type SecretKey struct {
	X *pbc.Element // x
}

// FileMetadata 存储文件的元信息
type FileMetadata struct {
	Name      string
	NumBlocks int
}

// Proof CSP生成的持有证明
type Proof struct {
	Sigma *pbc.Element // 聚合后的标签
	Mu    *pbc.Element // 聚合后的盲化数据
	T     *pbc.Element // 用于盲化的辅助值
	Tau   string       // 当前状态，用于生成非交互式挑战
	C     int          // 挑战的块数量
}

// NewSystemParams 初始化系统参数
func NewSystemParams() (*SystemParams, error) {
	// 使用 type A pairing
	params := pbc.GenerateA(160, 512)
	pairing := params.NewPairing()

	g := pairing.NewG1().Rand()
	u := pairing.NewG1().Rand()

	return &SystemParams{
		Pairing: pairing,
		G:       g,
		U:       u,
	}, nil
}

// H 实现了论文中的哈希函数 H: {0,1}* -> G1
func (sp *SystemParams) H(data []byte) *pbc.Element {
	return sp.Pairing.NewG1().SetFromHash(data)
}

// h 实现了论文中的哈希函数 h: GT -> Zp
func (sp *SystemParams) h(element *pbc.Element) *pbc.Element {
	hash := sha256.Sum256(element.Bytes())
	return sp.Pairing.NewZr().SetFromHash(hash[:])
}

// F 实现了论文中的伪随机函数 F: {0,1}* -> [0, n-1]
func (sp *SystemParams) F(tau string, i, n int) int {
	// 将状态和索引拼接作为输入
	input := []byte(fmt.Sprintf("%s||%d", tau, i))
	hash := sha256.Sum256(input)

	// 将哈希值转换为大整数，并取模
	val := new(big.Int).SetBytes(hash[:])
	mod := big.NewInt(int64(n))
	res := new(big.Int).Mod(val, mod)

	return int(res.Int64())
}

// splitFileIntoBlocks 将文件字节流切分为数据块，每个块作为Zp中的一个元素
func splitFileIntoBlocks(fileData []byte, blockSize int, pairing *pbc.Pairing) []*pbc.Element {
	var blocks []*pbc.Element
	numBlocks := len(fileData) / blockSize
	if len(fileData)%blockSize != 0 {
		numBlocks++
	}

	for i := 0; i < numBlocks; i++ {
		start := i * blockSize
		end := min(start + blockSize, len(fileData))

		// 将块数据转换为Zp元素
		blockBytes := fileData[start:end]
		blockElement := pairing.NewZr().SetFromHash(blockBytes)
		blocks = append(blocks, blockElement)
	}
	return blocks
}
